package com.zijing2333.adaptivescheduling.criticalNodeJudgment;

import android.util.Log;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;


public class DataReader extends DataAccessObject implements DataOperator{


    /**
     * used in sub thread, record http response
     */
    private static String execResult;


    /**
     * used in sub thread, record the target url
     */
    private static String url;


    public DataReader(String host, int port) {
        super(host, port);
        this.queryTarget = "/db/query";
        url = "http://" + host + ":" + port + queryTarget;
    }


    public DataReader(String host) {
        super(host);
        this.queryTarget = "/db/query";
        url = "http://" + host + ":" + port + queryTarget;
    }


    /**
     * this method is designed for sub-thread save its response
     */
    private synchronized void updateResponse(String response) {
        execResult = response;
    }


    /**
     * to judge a node id is valid or not
     */
    private boolean isNodeIdValid(int id) {
        String sql = "select "+ id + " from node where id = " + id;
        String resultJsonString = exec(sql);
        JSONObject arr = JSONObject.parseObject(resultJsonString);
        JSONArray resultArray = arr.getJSONArray("results");
        JSONArray valueArray = resultArray.getJSONObject(0).getJSONArray("values");
        return valueArray != null;
    }


    /**
     * get a node fundamental information via a node id
     * return a HashMap<String, Object><br>
     * e.g. "id" = 1, "ip" = "127.0.0.1"
     * @param id node id
     * @return a {@link HashMap} contains node information
     */
    public HashMap<String, Object> getNodeInfoByNodeId(int id) {
        HashMap<String, Object> result = new HashMap<>();
        String sql = "select * from NODE where id = " + id;
        return getParsedHashMap(result, sql);
    }


    /**
     * get a node the latest factor information via a node id
     * return a HashMap<br>
     * e.g. "cpuFreq" = 2400, etc.<br>
     * <font color = 'red'>P.S.</font>
     * this method should be called after the data is saved at least once, or the system will down.
     * @param dataOwner the node id
     * @return the factor {@link HashMap}
     */
    public HashMap<String, Object> getLatestFactorsByNodeId(int dataOwner) {
        if (!isNodeIdValid(dataOwner)) return null;
        HashMap<String, Object> result = new HashMap<>();
        String sql = "select " +
                "CPUFreq, " +
                "NOC, " +
                "CPURate, " +
                "CPUStructure, " +
                "GPUFlops, " +
                "IDLEVram, " +
                "GPURate, " +
                "UPLOADSpeed, " +
                "DOWNLOADSpeed, " +
                "PING, " +
                "PLR, " +
                "MEMORYFreq, " +
                "IDLERam, " +
                "EXTERNALStorage, " +
                "EMR, " +
                "EMW " +
                "from resource where owner = " + dataOwner + " order by time desc limit 1";
        return getParsedHashMap(result, sql);
    }


    private HashMap<String, Object> getParsedHashMap(HashMap<String, Object> result,
                                                     String sql) {
        String resultJsonString = exec(sql);
        JSONObject arr = JSONObject.parseObject(resultJsonString);
        JSONArray resultArray = arr.getJSONArray("results");
        JSONArray columnArray = resultArray.getJSONObject(0).getJSONArray("columns");
        JSONArray values = resultArray.getJSONObject(0).getJSONArray("values");
        if (values == null) return result;
        JSONArray valueArray = values.getJSONArray(0);
        for (int i = 0; i < columnArray.size(); ++i) {
            result.put(columnArray.getString(i), valueArray.get(i));
        }
        return result;
    }


    private Object getSingleValue(String resultJsonString) {
        JSONObject arr = JSONObject.parseObject(resultJsonString);
        JSONArray resultArray = arr.getJSONArray("results");
        JSONArray values = resultArray.getJSONObject(0).getJSONArray("values");
        if (values == null) return null;
        return values.getJSONArray(0).get(0);
    }


    private List<HashMap<String, Object>> getParsedHashMapList(List<HashMap<String, Object>> resultList,
                                                               String sql) {
        String resultJsonString = exec(sql);
        JSONObject arr = JSONObject.parseObject(resultJsonString);
        JSONArray resultArray = arr.getJSONArray("results");
        JSONArray columnArray = resultArray.getJSONObject(0).getJSONArray("columns");
        JSONArray valueArray = resultArray.getJSONObject(0).getJSONArray("values");
        if (valueArray == null) return resultList;
        for (int i = 0; i < valueArray.size(); ++i) {
            HashMap<String, Object> result = new HashMap<>();
            for (int j = 0; j < columnArray.size(); ++j) {
                result.put(columnArray.getString(j), valueArray.getJSONArray(i).get(j));
            }
            resultList.add(result);
        }
        return resultList;
    }


    /**
     * get task info item via task id and return a hashMap
     * @param taskId the certain task id
     * @return the Task instance {@link HashMap}
     */
    public HashMap<String, Object> getTaskInfoByTaskId(String taskId) {
        HashMap<String, Object> result = new HashMap<>();
        String sql = String.format("select * from TaskInfo where taskid = '%s'", taskId);
        return getParsedHashMap(result, sql);
    }


    /**
     * get all task param items via task group, paramtype and name
     * @param taskGroup the task group
     * @param paramType the param type
     * @param name the task param name
     * @return the {@link List} contains all task param {@link HashMap}
     */
    @Deprecated
    public List<HashMap<String, Object>> getTaskParamItems(String taskGroup, String paramType, String name) {
        List<HashMap<String, Object>> resultList = new ArrayList<>();
        String sql = String.format("select * from TaskParam where taskGroup = '%s' and paramType = '%s' and name = '%s'",
                taskGroup, paramType, name);
        return getParsedHashMapList(resultList, sql);
    }


    /**
     * get all task param items via task group, paramType, name and serviceId
     * @param taskGroup the task group
     * @param paramType the param type
     * @param name the task param name
     * @param serviceId the service id
     * @return the {@link List} contains all task param {@link HashMap}
     */
    public List<HashMap<String, Object>> getTaskParamItems(String taskGroup, String paramType, String name, String serviceId) {
        List<HashMap<String, Object>> resultList = new ArrayList<>();
        String sql = String.format("select * from TaskParam where taskGroup = '%s' and paramType = '%s' and name = '%s' and serviceId = '%s'",
                taskGroup, paramType, name, serviceId);
        return getParsedHashMapList(resultList, sql);
    }


    /**
     * get a unique task pram item
     * @param taskId the task id
     * @param taskGroup the task group
     * @param paramType the param type
     * @param name the task param name
     * @return the task param {@link HashMap}
     */
    public HashMap<String, Object> getTaskParamItem(String taskId, String taskGroup, String paramType, String name) {
        HashMap<String, Object> result = new HashMap<>();
        String sql = String.format(
                "select * from taskParam where taskId = '%s' and taskGroup = '%s' and paramType = '%s' and name = '%s",
                taskId,
                taskGroup,
                paramType,
                name
        );
        return getParsedHashMap(result, sql);
    }


    /**
     * get all task info in a certain task group
     * @param group the task group
     * @return a {@link List} contains all task info {@link HashMap}
     */
    @Deprecated
    public List<HashMap<String, Object>> getTaskInfoByGroup(String group) {
        List<HashMap<String, Object>> resultList = new ArrayList<>();
        String sql = String.format(
                "select * from taskInfo where taskGroup = '%s'",
                group
        );
        return getParsedHashMapList(resultList, sql);
    }


    /**
     * get all task info in a certain task group
     * @param serviceId the service id
     * @param group the task group
     * @return a {@link List} contains all task info {@link HashMap}
     */
    public List<HashMap<String, Object>> getTaskInfoByGroup(String serviceId, String group) {
        List<HashMap<String, Object>> resultList = new ArrayList<>();
        String sql = String.format(
                "select * from taskInfo where serviceId = '%s' and taskGroup = '%s'",
                serviceId,
                group
        );
        return getParsedHashMapList(resultList, sql);
    }


    /**
     * get all task priority
     * @return a {@link List} contains {@link HashMap} with taskId and priority
     */
    public List<HashMap<String, Object>> getTaskPriorities() {
        List<HashMap<String, Object>> resultList = new ArrayList<>();
        String sql = String.format(
                "select taskId, priority from TaskInfo"
        );
        return getParsedHashMapList(resultList, sql);
    }


    /**
     * get a unique predictor item via type and IMEI
     * @param type the type
     * @param IMEI the IMEI
     * @return the predictor data
     */
    public String getPredictorData(String type, String IMEI) {
        String sqlCode = String.format(
                "select data from Predictor where type = '%s' and IMEI = '%s' ",
                type,
                IMEI
        );
        String resultJsonString = exec(sqlCode);
        return (String) getSingleValue(resultJsonString);
    }


    /**
     * get data and IMEI in specific type
     * @param type the type
     * @return
     */
    public HashMap<String, Object> getPredictorDataAndIMEI(String type) {
        HashMap<String, Object> result = new HashMap<>();
        String sql = String.format(
                "select data, IMEI from Predictor where type = '%s'",
                type
        );
        return getParsedHashMap(result, sql);
    }


    public HashMap<String, Object> getPredictorData(int type) {
        HashMap<String, Object> result = new HashMap<>();
        String sql = String.format(
                "select data from Predictor where TYPE = '%d'",
                type
        );
        return getParsedHashMap(result, sql);
    }


    /**
     * <font color = 'red'>P.S.</font>
     * it is recommended not to use this method in SQL code related to data modification
     * to avoid increasing maintenance difficulty
     */
    @Override
    public String exec(String sql) {
        Thread httpThread = new Thread(()->{
            String intactUrl = url + "?q=" + sql;
            Request request = new Request.Builder()
                    .url(intactUrl).build();
            OkHttpClient client = new OkHttpClient();
            try {
                Response response = client.newCall(request).execute();
//                Log.d("OkHttp","response.code()=="+response.code());
                if(response.isSuccessful()){
                    //Log.d("OkHttp","response.code()=="+response.code());
                    //Log.d("OkHttp","response.message()=="+response.message());
                    updateResponse(response.body().string());
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        httpThread.start();
        try {
            httpThread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //Log.d("Reader-exec", execResult);
        return execResult;
    }


//    public static void main(String[] args) {
//        DataReader reader = new DataReader("localhost", 4001);
////        reader.getNodeInfoByNodeId(1).forEach((k,v)->System.out.println(k + " " + v));
////        System.out.println(reader.isNodeIdValid(2));
////        System.out.println(reader.exec("select * from node"));
////        System.out.println(reader.getLatestFactorsByNodeId(1));
//        System.out.println(reader.getNodeInfoByNodeId(1));
//    }
}
